﻿using System;
using System.Collections.Generic;
using Cinemachine;
using com.yoozoo.gta.Extension.UGUI.ScreenOrientationEx;
using Gameplay.PVE.Utils;
using UnityEngine;
using UnityEngine.Playables;
using WorldMap;
using Yoozoo.Gameplay.Liberty;
using Yoozoo.HRP.Runtime;
using Yoozoo.Mars.Got;

namespace Gameplay.PVE.Survivor
{
    public class CameraManager: BattleSingleton<CameraManager>
    {
        private BattleGameObject mainCamera;
        private Camera camera;
        private CinemachineVirtualCamera followVC;
        private bool cameraReady;

        private Vector3 originFollowOffset;
        private Vector3 originTrackOffset;
        
        private float originNearClipPlane;
        private float originFarClipPlane;

        private Vector3 originCameraForward;

        private Vector3 lastForward;

        private Vector3 originBossFollowOffset;
        private Vector3 originBossTrackOffset;

        public Camera HudCamera;

        public int standardHeight = 1650;
        public int standardWidth = 750;
        private CinemachineBrain cinemachineBrain;

        private Transform cameraFollow;
        public Transform detector;
        public SceneTransformDetector sceneDetector;
        
        public Camera MainCamera => camera;

        private float offsetByScreenHeightDistance = 0;

        private Dictionary<ECameraType, CinemachineVirtualCamera> cameraMap = new Dictionary<ECameraType, CinemachineVirtualCamera>();

        private Vector3 cameraCenter;
        
        private Vector3 GetOffsetByScreenHeight(Vector3 offset)
        {
            var height = Screen.height;
            var standardWidth = 1650f;
            if (ScreenOrientManager.GetInstance().IsPortrait())
            {
                standardWidth = 750;
            }
            var fixedHeight = standardWidth / Screen.width * height;
            var scale = fixedHeight / standardHeight;
            return offset * scale;
        }
        
		public void Initialize()
        {
            var cameraFollowGameObject = new GameObject("CameraFollow");
            cameraFollow = cameraFollowGameObject.transform;
            cameraFollow.parent = SurvivorManager.Instance.Root;
            cameraFollow.localPosition = Vector3.zero;
            if (cameraCenter != BattleConstValue.DEFAULT_POINT)
            {
                cameraFollow.position = cameraCenter;
            }
            
            detector = new GameObject("SceneDetector").transform;
            detector.parent = SurvivorManager.Instance.Root;
            sceneDetector = detector.AddComponent<SceneTransformDetector>();
            sceneDetector.detectorSize = new Vector3(100, 50, 100);
            sceneDetector.CameraOffset = 0;
            sceneDetector.m_Bounds = new Bounds
            {
                center = Vector3.zero,
                size = new Vector3(100, 50, 100)
            };
            SetDetectorPosition(BattleConstValue.DEFAULT_POINT,100);
            var path = "Assets/ResourcesAssets/Pve/Survivor/Prefabs/SurvivorCamera.prefab";
            mainCamera = BattlePoolManager.Instance.Get(path, OnCameraReady);
            mainCamera.Parent = SurvivorManager.Instance.Root;
        }

        public void OnCameraReady(GameObject gameObject)
        {
            var follow = gameObject.transform.Find("Follow");
            var vc = follow.gameObject.GetComponent<CinemachineVirtualCamera>();
            followVC = vc;
            followVC.Follow = cameraFollow;
            followVC.LookAt = cameraFollow;
            cinemachineBrain = gameObject.GetComponentInChildren<CinemachineBrain>();
            camera = gameObject.transform.Find("Camera").GetComponent<Camera>();
            cameraPerlin = followVC.GetCinemachineComponent<CinemachineBasicMultiChannelPerlin>();
            if (originNearClipPlane != 0)
            {
                followVC.m_Lens.NearClipPlane = originNearClipPlane;
            }
            if (originFarClipPlane != 0)
            {
                followVC.m_Lens.FarClipPlane = originFarClipPlane;
            }

            if (originFollowOffset != BattleConstValue.DEFAULT_POINT)
            {
                SetCameraFollowOffset(originFollowOffset);
            }
            else
            {
                originFollowOffset = followVC.GetCinemachineComponent<CinemachineTransposer>().m_FollowOffset;
            }
            if (originTrackOffset != BattleConstValue.DEFAULT_POINT)
            {
                followVC.GetCinemachineComponent<CinemachineComposer>().m_TrackedObjectOffset = originTrackOffset;
            }
            else
            {
                originTrackOffset = followVC.GetCinemachineComponent<CinemachineComposer>().m_TrackedObjectOffset;
            }
            HudCamera = gameObject.transform.Find("Camera/HudCamera").GetComponent<Camera>();
            HudCamera.fieldOfView = camera.fieldOfView;
            HCameraManager.EnableCamera(camera.GetComponent<HCamera>());
            HCameraManager.EnableCamera(HudCamera.GetComponent<HCamera>());
            BattleResourceManager.Instance.LoadComplete(ELoadingResourceType.Camera);
            Timers.inst.Add(0.1f,1, (o) =>
            {
                cameraChanged = true;
                cameraReady = true;
                FlyTextManager.Instance.SetCamera(HudCamera);
            });
            cameraMap.Clear();
            cameraMap.Add(ECameraType.Follow, followVC);
            var boss = gameObject.transform.Find("Boss");
            vc = boss.gameObject.GetComponent<CinemachineVirtualCamera>();
            originBossFollowOffset = vc.GetCinemachineComponent<CinemachineTransposer>().m_FollowOffset;
            originBossTrackOffset = vc.GetCinemachineComponent<CinemachineComposer>().m_TrackedObjectOffset;
            cameraMap.Add(ECameraType.Boss, vc);
            SwitchCamera(ECameraType.Follow);
            GuideLineManager.Instance.BindCamera(camera);
        }

        public bool CameraReady => cameraReady;
        public Vector3 CameraForward => originCameraForward;

        private CinemachineBasicMultiChannelPerlin cameraPerlin;
        private float shakeStartTime;
        private float shakeDuration;
        private float shakeRange;

        //真实值
        private Vector3 followOffset;

        private int lastScreenHeight;

        public void RefreshCamera()
        {
            followVC.MoveToTopOfPrioritySubqueue();
            if (StageManager.Instance.TeamGroup != null)
            {
                cameraFollow.position = StageManager.Instance.TeamGroup.Position;
            }
            cinemachineBrain.m_DefaultBlend.m_Style = CinemachineBlendDefinition.Style.Cut;
            cinemachineBrain.m_DefaultBlend.m_Time = 0;
            cinemachineBrain.ManualUpdate();
        }
        
        private void SetCameraFollowOffset(Vector3 offset)
        {
            followOffset = offset;
            var newOffset = GetOffsetByScreenHeight(offset);
            offsetByScreenHeightDistance = newOffset.magnitude - followOffset.magnitude;
            followVC.GetCinemachineComponent<CinemachineTransposer>().m_FollowOffset = newOffset;
            if (originNearClipPlane != 0)
            {
                followVC.m_Lens.NearClipPlane = Mathf.Max(originNearClipPlane + offsetByScreenHeightDistance,0.1f);
            }
            //RefreshCamera();
        }

        public void StartDeck(Vector3 center)
        {
            if (cameraFollow != null)
            {
                cameraFollow.position = center;
            }
            cameraCenter = center;
        }
        
        public void Update()
        {
            if (cameraPerlin == null)
            {
                return;
            }
            if (StageManager.Instance.TeamGroup != null)
            {
                cameraFollow.position = StageManager.Instance.TeamGroup.Position;
            }
            if (shakeStartTime != 0)
            {
                if (Time.time - shakeStartTime >= shakeDuration)
                {
                    StopShake();
                }
                else
                {
                    cameraPerlin.m_AmplitudeGain = shakeRange * 50f;
                    cameraPerlin.m_FrequencyGain = 1;
                }
            }

            if (changeFollowStartTime > 0)
            {
                var time = Time.unscaledTime - changeFollowStartTime;
                if (time < followStartTime)
                {
                    var t = time / followStartTime;
                    t = Mathf.Clamp(t, 0, 1);
                    SetCameraFollowOffset(Vector3.Lerp(startFollowOffset, targetFollowOffset, t));
                }
                else if (time < followStartTime + followHoldTime)
                {
                    
                }
                else if (time < followStartTime + followHoldTime + followBackTime)
                {
                    var t = (time - followStartTime - followHoldTime) / followBackTime;
                    t = Mathf.Clamp(t, 0, 1);
                    SetCameraFollowOffset(Vector3.Lerp(targetFollowOffset,startFollowOffset,t));
                }
                else
                {
                    changeFollowStartTime = 0;
                }
            }

            if (changeTrackStartTime > 0)
            {
                var time = Time.unscaledTime - changeTrackStartTime;
                if (time < trackStartTime)
                {
                    var t = time / trackStartTime;
                    t = Mathf.Clamp(t, 0, 1);
                    followVC.GetCinemachineComponent<CinemachineComposer>().m_TrackedObjectOffset = Vector3.Lerp(startTrackOffset, targetTrackOffset, t);
                }
                else if (time < trackStartTime + trackHoldTime)
                {
                    
                }
                else if (time < trackStartTime + trackHoldTime + trackBackTime)
                {
                    var t = (time - trackStartTime - trackHoldTime) / trackBackTime;
                    t = Mathf.Clamp(t, 0, 1);
                    followVC.GetCinemachineComponent<CinemachineComposer>().m_TrackedObjectOffset = Vector3.Lerp(targetTrackOffset,startTrackOffset,t);
                }
                else
                {
                    changeTrackStartTime = 0;
                }
            }

            if (Screen.height != lastScreenHeight)
            {
                lastScreenHeight = Screen.height;
                SetCameraFollowOffset(followOffset);
            }

            if (cameraReady)
            {
                detector.transform.position = cameraFollow.transform.position;
            }
        }

        public void LateUpdate()
        {
            if (!cameraReady)
            {
                return;
            }
            if (cameraChanged || lastForward != camera.transform.forward)
            {
                lastForward = camera.transform.forward;
                originCameraForward = camera.transform.forward;
                cameraChanged = false;
                UnitManager.Instance.OnCameraChanged();
            }
        }
        
        
        public void Shake(float time,float range = 2,float shakeInterval = 0.02f)
        {
            shakeStartTime = Time.time;
            shakeDuration = time;
            shakeRange = range / 20f;
        }
        
        public void StopShake()
        {
            cameraPerlin.m_AmplitudeGain = 0;
            cameraPerlin.m_FrequencyGain = 0;
            shakeStartTime = 0;
        }

        private Vector3 startFollowOffset;
        private Vector3 targetFollowOffset;
        private float changeFollowStartTime;
        private float followStartTime;
        private float followHoldTime;
        private float followBackTime;
        
        private Vector3 startTrackOffset;
        private Vector3 targetTrackOffset;
        private float changeTrackStartTime;
        private float trackStartTime;
        private float trackHoldTime;
        private float trackBackTime;

        private bool cameraChanged = false;

        public void SetCameraFollow(Vector3 follow)
        {
            originFollowOffset = follow;
            if (camera != null)
            {
                SetCameraFollowOffset(follow);
                cameraChanged = true;
            }
        }
        
        public void SetCameraTrack(Vector3 track)
        {
            originTrackOffset = track;
            if (camera != null)
            {
                followVC.GetCinemachineComponent<CinemachineComposer>().m_TrackedObjectOffset = track;
                cameraChanged = true;
            }
        }

        public void SetCameraNearFar(float near,float far)
        {
            originNearClipPlane = near;
            originFarClipPlane = far;
            if (camera != null)
            {
                followVC.m_Lens.NearClipPlane = Math.Max(near + offsetByScreenHeightDistance,0.1f);
                followVC.m_Lens.FarClipPlane = far;
            }
        }
        
        public void ChangeCameraFollow(Vector3 offset, float startTime,float holdTime, float backTime)
        {
            startFollowOffset = followOffset;//followVC.GetCinemachineComponent<CinemachineTransposer>().m_FollowOffset;
            targetFollowOffset = offset;
            changeFollowStartTime = Time.unscaledTime;
            followStartTime = startTime;
            followBackTime = backTime;
            followHoldTime = holdTime;
        }
        
        public void ChangeCameraTrack(Vector3 offset, float startTime,float holdTime, float backTime)
        {
            startTrackOffset = followVC.GetCinemachineComponent<CinemachineComposer>().m_TrackedObjectOffset;
            targetTrackOffset = offset;
            changeTrackStartTime = Time.unscaledTime;
            trackStartTime = startTime;
            trackBackTime = backTime;
            trackHoldTime = holdTime;
        }

        public void OnTimelineEnd()
        {
            HCameraManager.EnableCamera(HudCamera.GetComponent<HCamera>());
            HCameraManager.EnableCamera(camera.GetComponent<HCamera>());
        }

        public void OnTimelineStart()
        {
            HCameraManager.DisableCamera(HudCamera.GetComponent<HCamera>());
            HCameraManager.DisableCamera(camera.GetComponent<HCamera>());
        }

        public void StartTimeline(PlayableDirector director)
        {
            HCameraManager.DisableCamera(HudCamera.GetComponent<HCamera>());
            var playableAsset = director.playableAsset;
            CinemachineTrack cameraTrack = null;
            foreach (var bind in playableAsset.outputs)
            {
                if (bind.outputTargetType == typeof(CinemachineBrain))
                {
                    cameraTrack = bind.sourceObject as CinemachineTrack;
                    director.SetGenericBinding(cameraTrack, cinemachineBrain);
                    foreach (var clip in cameraTrack.GetClips())
                    {
                        if (clip.asset is CinemachineShot shot)
                        {
                            shot.VirtualCamera = new ExposedReference<CinemachineVirtualCameraBase>
                            {
                                defaultValue = camera
                            };
                        }
                    }
                }
            }

            if (cameraTrack != null)
            {
                director.SetGenericBinding(cameraTrack, cinemachineBrain);
                /*foreach (var clip in cameraTrack.GetClips())
                {
                    if (clip.asset is CinemachineShot shot)
                    {
                        var camera = PveCameraManager.Instance.GetRpgCameraByName(clip.displayName);
                        if (camera != null)
                        {
                            shot.VirtualCamera = new ExposedReference<CinemachineVirtualCameraBase>
                            {
                                defaultValue = camera
                            };
                            if (clip.displayName == "BattleBoss")
                            {
                                bossFixedCamera = camera;
                            }
                            else if (clip.displayName == "BossCome")
                            {
                                bossComeCamera = camera;
                            }
                        }
                       
                    }
                }*/
            }
        }
        
        public Vector3[] GetCorners(float distance)
        {
            var mainCamera = camera;
            Vector3[] corners = new Vector3[4];

            float halfFOV = (mainCamera.fieldOfView * 0.5f) * Mathf.Deg2Rad;
            float aspect = mainCamera.aspect;

            float height = distance * Mathf.Tan(halfFOV);
            float width = height * aspect;

            // UpperLeft
            corners[0] = mainCamera.transform.position - (mainCamera.transform.right * width);
            corners[0] += mainCamera.transform.up * height;
            corners[0] += mainCamera.transform.forward * distance;

            // UpperRight
            corners[1] = mainCamera.transform.position + (mainCamera.transform.right * width);
            corners[1] += mainCamera.transform.up * height;
            corners[1] += mainCamera.transform.forward * distance;

            // LowerLeft
            corners[2] = mainCamera.transform.position - (mainCamera.transform.right * width);
            corners[2] -= mainCamera.transform.up * height;
            corners[2] += mainCamera.transform.forward * distance;

            // LowerRight
            corners[3] = mainCamera.transform.position + (mainCamera.transform.right * width);
            corners[3] -= mainCamera.transform.up * height;
            corners[3] += mainCamera.transform.forward * distance;
            
            return corners;
        }

        public void SetDrawLineWidthAndHeight()
        {
            /*Vector3[] planeConrners = CameraManager.Instance.GetCorners(0.1f);
            Vector3 pos = camera.transform.position;
            var m_CornersPosition0 = WorldMapUtility.GetIntersectionWithLineAndPlane(pos, pos - planeConrners[0],
                Vector3.up, Vector3.zero);
            var m_CornersPosition1 = WorldMapUtility.GetIntersectionWithLineAndPlane(pos, pos - planeConrners[1],
                Vector3.up, Vector3.zero);
            var m_CornersPosition2 = WorldMapUtility.GetIntersectionWithLineAndPlane(pos, pos - planeConrners[2],
                Vector3.up, Vector3.zero);
            var m_CornersPosition3 = WorldMapUtility.GetIntersectionWithLineAndPlane(pos, pos - planeConrners[3],
                Vector3.up, Vector3.zero);

            float tempMaxHeight = Mathf.Abs(m_CornersPosition0.z - m_CornersPosition2.z);
            float tempMaxWidth = Mathf.Abs(m_CornersPosition0.x - m_CornersPosition1.x);
            var MaxHeight = tempMaxHeight;
            var MaxWidth = tempMaxWidth;*/
            if (DrawLineTool.GetInstance().IsInitialized())
            {
                DrawLineTool.GetInstance().SetLayer(LayerMask.NameToLayer("AgentHair"));
                DrawLineTool.GetInstance().SetHeight(0.1f);
                var MaxWidth = 30f;
                var MaxHeight = 30f;
                //BattleDebug.LogError("设置行军线裁剪面 " + MaxWidth + " " + MaxHeight + " 相机位置 " + camera.transform.position);
                DrawLineTool.GetInstance().SetWHRect(MaxWidth, MaxHeight,MaxHeight / 3);
            }
        }

        public void SwitchCamera(ECameraType cameraType,Transform follow = null,Vector3 forward = default,float lerpTime = 1f)
        {
            if (forward == default)
            {
                forward = -camera.transform.forward;
                forward.y = 0;
            }
            //var cameraForward = camera.transform.forward;
            //cameraForward.y = 0;
            foreach (var item in cameraMap)
            {
                if (item.Key == cameraType)
                {
                    if (follow != null)
                    {
                        item.Value.Follow = follow;
                        item.Value.LookAt = follow;
                    }
                    if (cameraType == ECameraType.Boss)
                    {
                        cinemachineBrain.m_DefaultBlend.m_Style = CinemachineBlendDefinition.Style.EaseInOut;
                        cinemachineBrain.m_DefaultBlend.m_Time = lerpTime;
                        var newOffset = GetOffsetByScreenHeight(originBossFollowOffset);
                        var angle = PveUtils.GetAngle(Vector3.forward, forward);
                        item.Value.GetCinemachineComponent<CinemachineTransposer>().m_FollowOffset = Quaternion.AngleAxis(angle,Vector3.up) * newOffset;
                        item.Value.GetCinemachineComponent<CinemachineComposer>().m_TrackedObjectOffset = Quaternion.AngleAxis(angle,Vector3.up) * originBossTrackOffset;
                    }
                    item.Value.Priority = 10;
                }
                else
                {
                    item.Value.Priority = 0;
                }
            }
        }

        /*public void SetDetectorParent(Transform parent,float size)
        {
            //detector.parent = parent;
            //detector.localPosition = Vector3.zero;
            //detector.localEulerAngles = Vector3.zero;
            sceneDetector.SetSize(size);
            LibertySceneManager.RefreshDetectorImmediately(sceneDetector);
        }*/

        public void SetDetectorPosition(Vector3 position,float size)
        {
            if (position == BattleConstValue.DEFAULT_POINT)
            {
                //sceneDetector.transform.localPosition = Vector3.zero;
            }
            else
            {
                sceneDetector.transform.position = position;
            }

            sceneDetector.BoundsDirty = true;
            sceneDetector.SetSize(size);
            LibertySceneManager.RefreshDetectorImmediately(sceneDetector);
        }

        public void SetHudCameraActive(bool active)
        {
            if (active)
            {
                HCameraManager.EnableCamera(HudCamera.GetComponent<HCamera>());
            }
            else
            {
                HCameraManager.DisableCamera(HudCamera.GetComponent<HCamera>());
            }
        }
        
        public override void Dispose()
        {
            if (mainCamera != null)
            {
                HCameraManager.DisableCamera(camera.GetComponent<HCamera>());
                HCameraManager.DisableCamera(HudCamera.GetComponent<HCamera>());
                mainCamera.Dispose();
                camera = null;
                HudCamera = null;
                mainCamera = null;
                followVC = null;
                cameraReady = false;
            }
            cameraFollow = null;
            startFollowOffset = Vector3.zero;
            targetFollowOffset = Vector3.zero;
            changeFollowStartTime = 0;
            followStartTime = 0;
            followHoldTime = 0;
            followBackTime = 0;
            startTrackOffset = Vector3.zero;
            targetTrackOffset = Vector3.zero;
            changeTrackStartTime = 0;
            trackStartTime = 0;
            trackHoldTime = 0;
            trackBackTime = 0;
            cameraCenter = BattleConstValue.DEFAULT_POINT;
        }
    }
}
